Skip to content

fix(login): web login-flow triage 2026-06-12 (F7/F1/F3/F4/F6/F10)#220

Merged
ahmetabdullahgultekin merged 5 commits into
mainfrom
fix/login-flow-triage-2026-06-12
Jun 12, 2026
Merged

fix(login): web login-flow triage 2026-06-12 (F7/F1/F3/F4/F6/F10)#220
ahmetabdullahgultekin merged 5 commits into
mainfrom
fix/login-flow-triage-2026-06-12

Conversation

@ahmetabdullahgultekin

Copy link
Copy Markdown
Contributor

Fixes the web login-flow bugs from the 2026-06-12 triage (docs/LOGIN_TRIAGE_2026-06-12.md). All changes are SHARED code → each fix lands on BOTH app.fivucsas (LoginPage/TwoFactorDispatcher) and verify.fivucsas (HostedLoginApp/LoginMfaFlow) at once. Separate, revertible commit per fix. NO hardcoded UI strings — every new string via t() with both en.json + tr.json.

Fixes

F7 — picker offered non-renderable methods (cc8e0e5)

MethodPickerStep offered APPROVE_LOGIN / PASSKEY, which have no MfaStepRenderer case → selecting one hit the renderer's default: "Bilinmeyen yöntem: APPROVE_LOGIN" dead-end with a blank icon. These are device-implicit Layer-1 factors with dedicated first-factor entry points (PasskeyLoginButton / ApproveLoginPanel via Layer1Shortcuts), not generic MFA steps.

  • How the picker is gated: new NON_RENDERABLE_METHODS set; the offered list is filtered (.filter(m => !NON_RENDERABLE_METHODS.has(m.methodType))) BEFORE sort/render, so the card never appears — not just re-worded. The dedicated Layer-1 shortcuts are untouched.
  • Added passkey + approve_login icons to StepProgress.helpers METHOD_ICONS so any other render path shows an icon, not a blank avatar.

F1 + F3 (+ passkey-success behind F2) — usernameless Layer-1 → MFA continuation dead-end (ba71717)

On APPROVED-without-accessToken, QrLoginPanel set phase='mfa' and rendered only an info alert + back button, dropping the poll's mfaSessionToken (in-code TODO). ApproveLoginPanel had no multi-step branch; PasskeyLoginButton handed a tokenless response to onSuccess → "missing access token" dead-end.

  • How the continuation is threaded: new shared Layer1Continuation type. Each first-factor entry point now calls a new onMfaRequired(mfaSessionToken + next-step availableMethods + completedMethods + step counters) when Layer 1 approved but more steps remain. Poll types (QrLoginPoll, ApproveLoginPoll) gained availableMethods + completedMethods.
    • app.fivucsas (LoginPage): handleLayer1MfaRequired sets the session token + methods and enters the EXISTING MethodPickerStep (or single-method TwoFactorDispatcher).
    • verify.fivucsas (HostedLoginApp + LoginMfaFlow): new initialContinuation prop resumes LoginMfaFlow into its picker / first MFA step.
  • The back-button-only dead-end is removed. One fix resolves F1, F3, and passkey-success-into-MFA.

F4 — "approve from another device" mis-grouped (aa56d38)

Layer1Shortcuts now splits into two groups: truly no-typing (passkey + QR) first, then a separate labelled "Approve from another device" section whose button reads "…— enter your email", so the email step is expected. New keys approveLogin.sectionLabel + approveLogin.buttonWithEmail (en + tr).

F6 — NFC web copy clarification (5a1b295)

Reworded mfa.nfc.notSupported (en + tr): scanning in the browser is Chrome-on-Android only; on other devices enrol/manage the NFC card in the FIVUCSAS mobile app or pick another method. Does not claim a native NFC login exists (verified: none does).

F10 — face-detect rAF [Violation] (2595e4b)

useFaceDetection ran ML inference on every requestAnimationFrame on the main thread. Added a timestamp gate (MIN_DETECT_INTERVAL_MS = 55ms ≈ 18fps) to all three detection loops — inference is skipped on intervening frames; the rAF cadence/cleanup and detection/captureReady are unchanged. Not a Worker/OffscreenCanvas rewrite (out of scope).

Verification

  • npm test (vitest): 122 files / 1211 tests green (2 files / 25 tests skipped, pre-existing).
  • SKIP_MODEL_FETCH=1 npm run build: passes (tsc clean + vite build OK). Chunk-size + onnxruntime eval warnings are pre-existing dependency-level.

Deploy note

Merging auto-deploys app.fivucsas (Hostinger) — which also brings #219 live, resolving the F10 autocomplete deploy-lag. verify.fivucsas (Hetzner container) needs its separate rebuild to pick these up.

…approve icons

The arbitrary first-factor / MFA picker offered APPROVE_LOGIN and PASSKEY,
which have NO MfaStepRenderer case → selecting one routed into the renderer's
"Unknown authentication method: APPROVE_LOGIN" dead-end with a blank icon (F7).

These two are device-implicit Layer-1 factors with DEDICATED first-factor entry
points (PasskeyLoginButton / ApproveLoginPanel via Layer1Shortcuts) — not generic
mid-flow MFA steps. The backend may still list them in availableMethods
(e.g. ensureApproveLoginEnrollment auto-enrolls APPROVE_LOGIN), so MethodPickerStep
now FILTERS them out of the offered list (NON_RENDERABLE_METHODS) rather than
rendering an unusable card. The dedicated shortcuts are untouched.

Also add passkey + approve_login icons to StepProgress.helpers METHOD_ICONS so
any other place they render shows an icon instead of a blank avatar.

Shared code → fixes BOTH app.fivucsas and verify.fivucsas.
…o dead-end)

When a usernameless / cross-device Layer-1 method (QR sign-in, approve-on-another-
device, or passkey-as-first-factor) satisfied the first factor but the tenant flow
needed MORE steps, the server returned an mfaSessionToken WITHOUT final tokens —
but the web dropped it:
 - QrLoginPanel set phase='mfa' and rendered only an info alert + back button
   (continue suppressed), explicitly DROPPING the poll's mfaSessionToken (in-code
   TODO). → F1 ("web does nothing") = F3 ("only a back button").
 - ApproveLoginPanel had no multi-step branch at all.
 - PasskeyLoginButton handed a tokenless response to onSuccess → "missing access
   token" dead-end (the passkey-success-into-MFA case behind F2).

Fix (one mechanism): a shared Layer1Continuation handoff. Each first-factor entry
point now calls onMfaRequired(mfaSessionToken + next-step availableMethods) when
Layer 1 approved but more steps remain; the host enters the EXISTING MethodPicker /
MfaStepRenderer continuation:
 - app.fivucsas (LoginPage): handleLayer1MfaRequired sets the session + methods and
   shows the picker / single-method dispatcher.
 - verify.fivucsas (HostedLoginApp + LoginMfaFlow): new initialContinuation prop
   resumes LoginMfaFlow into the picker / first MFA step.

Poll/response types (QrLoginPoll, ApproveLoginPoll) gain availableMethods +
completedMethods. The back-button-only dead-end is removed.

Shared code → fixes BOTH surfaces.
… cluster

"Approve on another device" (APPROVE_LOGIN number-matching) sat visually
identical to passkey + QR in the no-typing usernameless cluster, but it is
genuinely NOT usernameless — it needs an identifier to route the push (backend
set supports_usernameless=false in V74), so pressing it then asked for an email,
which felt out of place (F4).

Layer1Shortcuts now splits the shortcuts into two groups: the truly no-typing
methods (passkey + QR) first, then a separate labelled "Approve from another
device" section whose button says "…— enter your email", so the later email
step is expected. New i18n keys approveLogin.sectionLabel + buttonWithEmail
(en + tr).

Shared code → fixes BOTH surfaces.
The mfa.nfc.notSupported message said NFC works only on Android Chrome — true for
the Web NFC API but incomplete. Reworded (en + tr) to point users to the FIVUCSAS
mobile app for managing their NFC card on other devices, or to pick another
method. Does NOT claim a native NFC *login* exists (verified: none does — mobile
NFC is enrol/eMRTD only).

Rendered by NfcStep (shared) → both surfaces.
…tion)

useFaceDetection ran synchronous ML inference on EVERY requestAnimationFrame on
the main thread (~60fps), producing `[Violation] requestAnimationFrame handler
took <N>ms` warnings + visible jank during the face step (F10).

Low-risk fix: add a timestamp gate (MIN_DETECT_INTERVAL_MS = 55ms ≈ 18fps) to
all three detection loops (FaceLandmarker / BlazeFace / MediaPipe FaceDetector).
The rAF cadence (and cleanup/cancellation) is unchanged; inference is just
SKIPPED on intervening frames. Detection / centering / captureReady are
unaffected. NOT a Worker/OffscreenCanvas rewrite (deliberately out of scope).
@ahmetabdullahgultekin ahmetabdullahgultekin merged commit 42158fa into main Jun 12, 2026
4 checks passed
@ahmetabdullahgultekin ahmetabdullahgultekin deleted the fix/login-flow-triage-2026-06-12 branch June 12, 2026 13:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant